-- FateChainM
-- Created by cheny3
-- 命运的链模块

module("FateChainM", package.seeall);

local fateChainGroupTable = {};
local propFates = {};
local fateChainType = {};
local typeGroupList = {};
local fateChainRelation = {};
local fateBabyTable = {};
local playerList = {};
local curState = FATE_CHAIN_STATE_DISABLE;
local leftTime = 0;
local FATE_LINK_AUTO_RECOVER = 28800;
local SEARCH_LIMIT_NUM = 10;    -- 只要显示5个，但是这边取10个，然后做一个距离排序，取最近的5个
local SHOW_LIMIT_NUM = 5;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 载入任务配置表
    fateChainGroupTable = {};
    fateChainType = {};
    typeGroupList = {};
    fateBabyTable = {};

    local fate_chain_group_CSV = get_fate_chain_group_CSV();
    for i = 1, #fate_chain_group_CSV do
        local id = fate_chain_group_CSV[i].id;
        local fate_type = fate_chain_group_CSV[i].type;
        fateChainGroupTable[id] = fate_chain_group_CSV[i];

        -- 整理类型
        if table.indexOf(fateChainType, fate_type) < 0 then
            table.insert(fateChainType, fate_type);
        end

        -- 按类型整理ID
        typeGroupList[fate_type] = typeGroupList[fate_type] or {};
        table.insert(typeGroupList[fate_type], id);

        local info = fateChainGroupTable[id];
        if info["bonus"]["props"] then
            table.insert(propFates, id);
        end
    end
    fate_chain_group_CSV = nil;

    -- 宝宝列表
    local fate_baby_CSV = get_fate_baby_CSV();
    for i = 1, #fate_baby_CSV do
        local id = fate_baby_CSV[i].id;
        fateBabyTable[id] = fate_baby_CSV[i];
    end
    fate_baby_CSV = nil;
end

-- 模块初始化
function init()
    -- 载入配置表
    loadCsv();

    EventMgr.removeAll("FateChainM");
    -- 注册连接断开事件
    EventMgr.register("FateChainM", event.NETWORK_DISCONNECTED, onDisconnect);
end

-- 断开链接
onDisconnect = function()
    -- 设置为失效
    curState = FATE_CHAIN_STATE_DISABLE;

    playerList = {};
end

-- 检索配置
function queryGroup(id, path)
    if not fateChainGroupTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return fateChainGroupTable[id];
    end

    return fateChainGroupTable[id][path];
end

-- 获得所有类型
function getAllGroupType()
    return fateChainType;
end

-- 获取某个类型所有组合
function getAllGroupByType(groupType)
    return typeGroupList[groupType] or {};
end

-- 计算链接奖励
function calcFateChainBonus(id)
    local ret = {};
    local bonus = queryGroup(id, "bonus");
    local args = queryGroup(id, "args");
    local prefix = "formula_";

    -- 如果配置了公式，则调用公式计算，否则直接取配置的值
    if type(bonus) == "string" and
       string.startWith(bonus, "formula_") then
       local formulaName = string.sub(bonus, string.len(prefix) + 1);
       ret = FormulaM.invoke(formulaName, args);
    elseif type(bonus) == "table" then
       ret = bonus;
    end

    if #ret > 0 and type(ret[1]) == "number" then
        ret = {ret, };
    end

    return ret;
end

-- 获取当前状态
function getCurState()
    return curState;
end

-- 玩家当前能否请求链接
function canRequestFateChain(targetRid)
    if curState ~= FATE_CHAIN_STATE_FREE then
        -- 不是空闲状态不允许
        return false;
    end

    if ME.user.dbase:query("fate_link", 0) <= 0 then
        -- 当前没有链接次数
        return false;
    end

    return true;
end

-- 玩家请求链接
function requestFateChain(targetRid)
    if true ~= canRequestFateChain(targetRid) then
        return false;
    end

    -- 把自己置为请求状态
    curState = FATE_CHAIN_STATE_REQUEST;

    Operation.cmd_request_fate_chain(targetRid);
    return true;
end

-- 玩家当前能否回答链接
function canAnswerFateChain(result)
    if curState ~= FATE_CHAIN_STATE_ANSWER then
        return false;
    end

    if result == 1 then
        -- 同意需要判断
        if ME.user.dbase:query("fate_link", 0) <= 0 then
            -- 当前没有链接次数
            return false;
        end
    end

    return true;
end

-- 玩家回答链接
function answerFateChain(result)
    if true ~= canAnswerFateChain(result) then
        return false;
    end

    Operation.cmd_answer_fate_chain(result);

    -- 回答完就置为空闲
    curState = FATE_CHAIN_STATE_FREE;

    return true;
end

-- 取消请求
function cancelRequest()
    -- 置为空闲
    curState = FATE_CHAIN_STATE_FREE;

    Operation.cmd_cancel_fate_chain();
end

-- 是否完成了链接
function isFateChainFinish(id)
    return BuffDataM.query("fate_chain_list", id) > FATE_CHAIN_UNFINISH;
end

-- 是否领取了链接
function isFateChainBonus(id)
    return BuffDataM.query("fate_chain_list", id) >= FATE_CHAIN_BONUS;
end

-- 是否已经提示了
function isFateChainTip(id)
    return BuffDataM.query("fate_chain_list", id) == FATE_CHAIN_TIP;
end

-- 能否领奖
function canTakeFateChainBonus(id)
    if not isFateChainFinish(id) then
        return false;
    end

    if isFateChainBonus(id) then
        return false;
    end

    return true;
end

-- 请求领奖
function takeFateChainBonus(id)
    if true ~= canTakeFateChainBonus(id) then
        return false;
    end

    Operation.cmd_take_fate_chain_bonus(id);
    return true;
end

-- 更新关系列表
function updateFateChainRelation(relation)
    fateChainRelation = relation;
end

-- 通知领取了奖励
function takeFateChainBonusSuccess(bonus, id)
    -- 设置领取过了
    BuffDataM.set("fate_chain_list", id, FATE_CHAIN_BONUS);

    -- 抛出领取成功
    EventMgr.fire(event.FATE_CHAIN_TAKE_BONUS, {["bonus"] = bonus, ["id"] = id,});

    -- 属性类的要尝试刷新一下
    local info = queryGroup(id, "bonus") or {};
    if not info["props"] then
        return;
    end

    if info["props"]["*"] then
        -- 全部刷
        local pets = ME.user.pets;
        for _, pet in pairs(pets) do
            PropM.refresh(pet);
        end
    else
        for petId, _ in pairs(info["props"]) do
            local pet = PetM.getMyPet(petId);
            if pet then
                PropM.refresh(pet);
            end
        end
    end
end

-- 通知链接成功
function fateChainSuccess(info)
    if info["fate_id"] then
        -- 设置链接
        BuffDataM.set("fate_chain_list", info["fate_id"], FATE_CHAIN_FINISH);
    end

    -- 选择对方RID
    local recordRid = info["request_rid"];
    if recordRid == ME.user.rid then
        recordRid = info["answer_rid"];
    end

    -- 增加关系
    addRelationship(recordRid, 1);

    -- 把自己置为空闲状态
    curState = FATE_CHAIN_STATE_FREE;

    -- 抛出事件
    EventMgr.fire(event.FATE_CHAIN_SUCCESS, {["info"] = info});
end

-- 增加和对方的关系
function addRelationship(recordRid, add)
    add = add or 1;
    fateChainRelation[recordRid] = tonumber(fateChainRelation[recordRid]) + add;
end

-- 获得和对方的关系
function getRelationship(recordRid)
    return tonumber(fateChainRelation[recordRid]);
end

-- 通知链接失败
function fateChainFail(reason)
    if curState == FATE_CHAIN_STATE_DISABLE then
        -- 玩家当前是失效的，那就不管了
        return;
    end

    -- 把自己置为空闲状态
    curState = FATE_CHAIN_STATE_FREE;

    -- 抛出事件
    EventMgr.fire(event.FATE_CHAIN_FAIL, {["reason"] = reason});
end

-- 收到链接请求
function receiveFateChainRequest(info)
    -- 把自己置为回答状态
    curState = FATE_CHAIN_STATE_ANSWER;

    -- 抛出事件
    EventMgr.fire(event.RECEIVE_FATE_CHAIN, {["info"] = info});
end

-- 玩家发起定位
function startFateChain()
    if curState ~= FATE_CHAIN_STATE_DISABLE or not Socket.isConnected() then
        -- 只有原本是失效的情况，才允许发起定位
        return;
    end

    -- 定位
    LocationM.start();

    -- 把自己设为空闲状态
    curState = FATE_CHAIN_STATE_FREE;
end

-- 发起搜索
function doSearch()
    LocationM.getNearbyPlayers(receiveData, SEARCH_LIMIT_NUM);
end

-- 排序
local sortData = function(a, b)
    return a["distance"] < b["distance"];
end

-- 收到周围玩家信息
function receiveData(info)
    playerList = info;

    -- 第一个是自己，需要去掉
    if #playerList > 0 and playerList[1]["rid"] == ME.user.rid then
        table.remove(playerList, 1);
    end

    if #playerList > SHOW_LIMIT_NUM then
        -- 超过限制数量了，排序一下，距离近的放前面
        table.sort(playerList, sortData);
        local ret = {};
        for i = 1, SHOW_LIMIT_NUM do
            ret[i] = playerList[i];
        end
        playerList = ret;
    end

    EventMgr.fire(event.NEARBY_INFO_ARRIVER, {["info"] = playerList});
end

-- 玩家退出
function endFateChain()
    -- 结束定位
    -- LocationM.endLocation();

    -- 把自己设置为失效
    Operation.cmd_set_fate_state_disable();

    -- 把状态设置为失效
    curState = FATE_CHAIN_STATE_DISABLE;

    playerList = {};
end

-- 能否显示入口
function canShowEnter()
    if ServerStatusM.query(MODULE_SWITCH_FATE_CHAIN) ~= 1 then
        -- 被禁止了
        return false;
    end

    -- UC和小米平台无法完整包更新，功能需要MODULE_SWITCH_UC_MI_FATE_CHAIN开启才能生效
    if PUBLISH_PLATFORM == PUBLISH_TYPE_UC or PUBLISH_PLATFORM == PUBLISH_TYPE_MI then
        if ServerStatusM.query(MODULE_SWITCH_UC_MI_FATE_CHAIN) ~= 1 then
            -- 被禁止了
            return false;
        end
    end

    -- 通关1-1
    return DungeonAreaM.isDungeonPassed(ME.user, 11);
end

-- 入口提示
function needTip()
    return hasBonusToTake();
end

-- 获得列表
function getPlayerList()
    return playerList;
end

-- 按类型获得列表/携带完成信息
function getFateListByType(fateType)
    local ret = {}
    local ids = getAllGroupByType(fateType) or {};
    for _, id in ipairs(ids) do
        local info = table.deepcopy(queryGroup(id));

        local group = info["group"] or {};
        for _, petId in ipairs(group) do
            if PetM.getMyPet(petId) then
                -- 有相关宠物的才显示
                info["state"] = BuffDataM.query("fate_chain_list", id);
                info["bonus"] = calcFateChainBonus(id);
                table.insert(ret, info);
                break;
            end
        end
    end

    return ret;
end

-- 获得当前链接信息
function getMyFateListInfo()
    local amount = 0;
    local finish = 0;
    local types = getAllGroupType();
    for _, fateType in ipairs(types) do
        local list = getFateListByType(fateType);
        amount = amount + #list;
        for _, info in ipairs(list) do
            if info["state"] >= FATE_CHAIN_FINISH then
                finish = finish + 1;
            end
        end
    end

    return {amount, finish};
end

-- 设置恢复剩余时间
function setFateLinkRecoveryLeftTime(t)
    leftTime = t;

    EventMgr.fire(event.FATE_LINK_LEFT_TIME);
end

-- 获取恢复剩余时间
function getFateLinkRecoveryLeftTime()
    return leftTime;
end

-- 链接次数上限
function getMaxLinkTimes()
    return FormulaM.invoke("CALC_FATE_LINK_MAX_TIMES");
end

-- 获取活力自动恢复总共需要的时间
function getFateRecoverTime()
    return FATE_LINK_AUTO_RECOVER;
end

-- 请求恢复
function tryFateLinkRecover()
    Operation.cmd_try_fate_link_recovery();
end

-- 获取命运之链增加的prop
function fetchFateProps(pet)
    local classId = pet.classId;
    local id;
    local ret = {};
    for _, id in ipairs(propFates) do
        repeat
            if not isFateChainBonus(id) then
                -- 还没领取
                break;
            end

            local propsInfo = queryGroup(id, "bonus")["props"] or {};
            if propsInfo["*"] then
                ret = table.append(ret, propsInfo["*"]);
            end

            if propsInfo[classId] then
                ret = table.append(ret, propsInfo[classId]);
            end
        until true
    end
    return ret;
end

-- 获取奖励描述
function getFateChainBonusDesc(id)
    local bonusDesc = queryGroup(id, "bonus_desc");
    if type(bonusDesc) == "string" and #bonusDesc > 0 then
        -- 配置了描述
        return bonusDesc;
    end

    local bonus = queryGroup(id, "bonus");
    if bonus["bonus"] then
        return getLineBreakBonusTip(bonus["bonus"]);
    end

    return "";
end

-- 所有的属性类奖励项目
function getPropFates()
    return propFates;
end

-- 是否有需要提示的宝宝
function getFateBabyTip()
    for _, id in ipairs(table.keys(fateBabyTable)) do
        if isFateChainBonus(id) and not isFateChainTip(id) then
            -- 已经领取了，但是还没有提示
            return id;
        end
    end

    return 0;
end

-- 玩家领奖提示
function setFateChainBonusTip(id)
    if not isFateChainBonus(id) then
        -- 还没领奖的不算
        return;
    end

    -- 设置提示过了
    BuffDataM.set("fate_chain_list", id, FATE_CHAIN_TIP);
    Operation.cmd_fate_bonus_tip(id);
end

-- 检索宝宝配置
function queryBaby(id, path)
    if not fateBabyTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return fateBabyTable[id];
    end

    return fateBabyTable[id][path];
end

-- 当前已经拥有的宝宝
function myFateBabyList()
    local ret = {};
    for _, id in ipairs(table.keys(fateBabyTable)) do
        if isFateChainBonus(id) then
            -- 已经领奖的
            table.insert(ret, id);
        end
    end
    return ret;
end

-- 奖励属性的详细信息
function getBonusDetailDesc(id)
    local detail = nil;

    local info = queryGroup(id);
    if type(info["bonus_desc"]) == "string" and string.len(info["bonus_desc"]) > 0 then
        detail = "[blue]" .. info["bonus_desc"] .. "[-]";
        local args = info["args"];
        if args["limit_pet"] and not PetM.getMyPet(args["limit_pet"]) then
            -- 没有宠物要加上这个提示
            detail = detail .. "\n" .. getLocStr("fate_gumball_detail");
        end
    end

    -- 没有就回复nil
    return detail;
end

-- 当前是否有可以领奖的项目
function hasBonusToTake()
    for _, id in ipairs(table.keys(fateChainGroupTable)) do
        if isFateChainFinish(id) and not isFateChainBonus(id) then
            return true;
        end
    end

    return false;
end

-- 当前某个分类是否有可以领取的项目
function hasBonusToTakeByType(fateType)
    local ids = getAllGroupByType(fateType) or {};
    for _, id in ipairs(ids) do
        if isFateChainFinish(id) and not isFateChainBonus(id) then
            return true;
        end
    end

    return false;
end